home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / bin / foomatic-printjob < prev    next >
Text File  |  2009-09-18  |  35KB  |  1,171 lines

  1. #!/usr/bin/perl
  2. # -*- perl -*-
  3. # This is foomatic-printjob, a program to print and manage printing
  4. # jobs with the same commands independent whether the spooler is CUPS,
  5. # LPD, LPRng, or PDQ.
  6.  
  7. # It also comprises half of a programattic API for user tools: you can
  8. # learn and control everything about the properties of printing jobs
  9. # here. With the sister program foomatic-configure, you can do
  10. # everything related to print queue static state: install, modify,
  11. # remove queues, query queue, printer, and driver info.
  12.  
  13. use Foomatic::Defaults;
  14. use Foomatic::DB;
  15.  
  16. # Read out the program name with which we were called, but discard the path
  17.  
  18. $0 =~ m!/([^/]+)\s*$!;
  19. $progname = $1;
  20.  
  21. # We use the library Getopt::Long here, so that we can have more than one "-o"
  22. # option on one command line.
  23.  
  24. use Getopt::Long;
  25. Getopt::Long::Configure("no_ignore_case", "pass_through");
  26. GetOptions("P=s" => \$opt_P,         # which queue (Printer)?
  27.        "d=s" => \$opt_d,         # which queue (Destination)?
  28.        "s=s" => \$opt_s,         # which Spooler?
  29.        "o=s" => \@opt_o,         # printing Options
  30.        "Q"   => \$opt_Q,         # Query jobs in queue
  31.        "R"   => \$opt_R,         # Remove job(s)
  32.        "C"   => \$opt_C,         # Control job(s)/queue(s)
  33.        "S"   => \$opt_S,         # set default Spooler
  34.        "h"   => \$opt_h);        # Help!
  35.  
  36. help() if ($opt_h && !$opt_P);
  37.  
  38. my $db = new Foomatic::DB;
  39.  
  40. my $in_config = {'queue'   => $opt_P,
  41.          'options' => \@opt_o,
  42.          'spooler' => $opt_s};
  43.  
  44. # Default action: Printing
  45. my $action = 'print';
  46.  
  47. # Determine the action by the name how we were called
  48. if ($progname =~ m!^lpc!) { # 'lpc*' ==> control 
  49.     $action = 'control';
  50. } elsif ($progname =~ m!^lprm!) { # 'lprm*' ==> remove jobs 
  51.     $action = 'remove';
  52. } elsif ($progname =~ m!^lpq!) { # 'lpq*' ==> list jobs 
  53.     $action = 'query';
  54. } elsif (($progname =~ m!^lpr!) || ($progname =~ m!^lp!)) { 
  55.     # 'lpr*', 'lp*' ==> print 
  56.     $action = 'print';
  57. }
  58.  
  59. # Determine the action by a command line option
  60. $action = ($opt_R ? 'remove' : $action);
  61. $action = ($opt_Q ? 'query' : $action);
  62. $action = ($opt_C ? 'control' : $action);
  63.  
  64. my $procs = { 'lpd' => { 'print'    => \&print_lpd,
  65.              'query'    => \&query_lpd,
  66.                  'remove'   => \&remove_lpd,
  67.                  'control'  => \&control_lpd },
  68.           'lprng'=>{ 'print'    => \&print_lprng,
  69.              'query'    => \&query_lprng,
  70.              'remove'   => \&remove_lprng,
  71.              'control'  => \&control_lpd },
  72.           'cups' =>{ 'print'    => \&print_cups,
  73.              'query'    => \&query_cups,
  74.              'remove'   => \&remove_cups,
  75.              'control'  => \&control_cups },
  76.           'pdq'  =>{ 'print'    => \&print_pdq,
  77.              'query'    => \&query_pdq,
  78.              'remove'   => \&remove_pdq,
  79.              'control'  => \&control_pdq } };
  80.  
  81. if (!(defined($in_config->{'queue'}))) {
  82.     # No job handling without knowing the name of the queue
  83.     # PRINTER environment variable
  84.     if (defined($opt_d)) {
  85.     $in_config->{'queue'} = $opt_d;
  86.     } elsif (defined($ENV{PRINTER})) {
  87.     $in_config->{'queue'} = $ENV{PRINTER};
  88.     } else {
  89.     # Use spoolers default
  90.     }
  91. }
  92.  
  93. if (!defined($in_config->{'spooler'})) {
  94.  
  95.     # Personal default spooler
  96.     if (($> != 0) && (-f "$ENV{'HOME'}/.defaultspooler")) {
  97.     $s = `cat $ENV{'HOME'}/.defaultspooler`;
  98.     chomp $s;
  99.     }
  100.     
  101.     # System default spooler
  102.     if ((!defined($s)) && (-f "$sysdeps->{'foo-etc'}/defaultspooler")) {
  103.     $s = `cat $sysdeps->{'foo-etc'}/defaultspooler`;
  104.     chomp $s;
  105.     }
  106.     
  107.     if (!defined($s)) {
  108.     $s = detect_spooler();
  109.     }
  110.  
  111.     die "Unable to identify spooler, please specify one with \"-s\"!\n"
  112.     unless $s;
  113.  
  114.     if (defined($opt_i)) {
  115.     print STDERR "You appear to be using $s.  Correct? ";
  116.     my $yn = <STDIN>;
  117.     die "\n" if ($yn !~ m!^y!i);
  118.     }
  119.  
  120.     $in_config->{'spooler'} = $s;
  121. }
  122.  
  123. if (defined($opt_S)) {
  124.     if ($> == 0) { # Program invoked as "root"?
  125.     # Set system default spooler
  126.     open DEFAULTFILE, "> $sysdeps->{'foo-etc'}/defaultspooler" ||
  127.         die "Cannot write $sysdeps->{'foo-etc'}/defaultspooler!\n";
  128.     print DEFAULTFILE "$in_config->{'spooler'}\n";
  129.     close DEFAULTFILE;
  130.     exit 0;
  131.     } else {
  132.     # Set personal default spooler
  133.     open DEFAULTFILE, "> $ENV{'HOME'}/.defaultspooler" ||
  134.         die "Cannot write $ENV{'HOME'}/.defaultspooler!\n";
  135.     print DEFAULTFILE "$in_config->{'spooler'}\n";
  136.     close DEFAULTFILE;
  137.     exit 0;
  138.     }
  139. }
  140.  
  141. # Exception...
  142. help_options($in_config) if ($opt_h);
  143.  
  144. # Call proper proc
  145. exit &{$procs->{$in_config->{'spooler'}}{$action}}($in_config);
  146.  
  147. ### Printing/Job manipulation functions for LPD
  148.  
  149. sub print_lpd {
  150.     my ($config) = $_[0];
  151.  
  152.     #sysdeps->{'lpd-lpr'} = "/home/test/lpr-0.71/lpr/lpr";
  153.  
  154.     # Auto-detect whether the "lpr" executable is the VA-Linux version or not
  155.     my $valinuxlpr = 
  156.     !(system "strings $sysdeps->{'lpd-lpr'} | grep option > /dev/null");
  157.  
  158.     # Printing command
  159.     my $commandline = "$sysdeps->{'lpd-lpr'}";
  160.  
  161.     # Add the printer queue argument
  162.     if (defined($config->{'queue'})) {
  163.     $commandline .= " -P $config->{'queue'}";
  164.     }
  165.  
  166.     # Add the driver-specific options supplied by the user, if any
  167.     # For the VA-Linux implementation of "lpr" (gnulpr) options are passed
  168.     # with '-o option=value -o switch', for the BSD implementation they are
  169.     # passe with '-J"option=value switch"'.
  170.     if ($valinuxlpr) {
  171.     # VA-Linux/gnulpr
  172.     if ($#{$config->{'options'}} >= 0) {
  173.         for (@{$config->{'options'}}) {
  174.         $commandline .= " -o $_";
  175.         }
  176.     }
  177.     } else {
  178.     # BSD
  179.     if ($#{$config->{'options'}} >= 0) {
  180.         $commandline .= " -J\"";
  181.         for (@{$config->{'options'}}) {
  182.         $commandline .= "$_ ";
  183.         }
  184.         $commandline .= "\"";
  185.     }
  186.     }
  187.  
  188.     # Add the remaining command line arguments, they are the names of
  189.     # the files to print and also spooler-specific options
  190.     $commandline .= " @ARGV";
  191.  
  192.     # Do it!
  193.  
  194.     #print "$commandline\n";
  195.     return (system $commandline) >> 8;
  196.  
  197. }
  198.  
  199. sub query_lpd {
  200.     my ($config) = $_[0];
  201.  
  202.     # standard lpq, emulate -a of lpq-cups
  203.     # Read additional options
  204.     GetOptions("a"   => \$opt_a);        # List jobs on all printers
  205.     
  206.     if (defined($opt_a)) {
  207.     # Get all printer queues
  208.     open QUEUELIST, "$sysdeps->{'lpd-lpc'} status 2>&1 | grep \":\$\" | ";
  209.     my @queuelist = <QUEUELIST>;
  210.     close QUEUELIST;
  211.     # List the jobs on all the queues
  212.     for (@queuelist) {
  213.         my $queue = $_;
  214.         chomp $queue;
  215.         print "$queue\n";
  216.         $queue =~ s/:$//;
  217.         my $result = (system "$sysdeps->{'lpd-lpq'} -P $queue @ARGV") >> 8;
  218.         if ($result != 0) {return $result};
  219.     }
  220.     } else {
  221.     # List the jobs on the specified queue
  222.     my $queue = "";
  223.     if (defined($config->{'queue'})) {
  224.         $queue = " -P $config->{'queue'}";
  225.     }
  226.     return (system "$sysdeps->{'lpd-lpq'}$queue @ARGV") >> 8;
  227.     }
  228. }
  229.  
  230. sub remove_lpd {
  231.     my ($config) = $_[0];
  232.     
  233.     # Remove a job with the standard "lprm" command
  234.  
  235.     # Removing command
  236.     my $commandline = "$sysdeps->{'lpd-lprm'}";
  237.  
  238.     # Add the printer queue argument
  239.     if (defined($config->{'queue'})) {
  240.     $commandline .= " -P $config->{'queue'}";
  241.     }
  242.  
  243.     # Add the remaining command line arguments, they are the numbers
  244.     # of the jobs to kill, the users whose jos to remove and also
  245.     # spooler-specific options
  246.  
  247.     $commandline .= " @ARGV";
  248.  
  249.     # Do it!
  250.  
  251.     #print "$commandline\n";
  252.     return (system $commandline) >> 8;
  253.  
  254. }
  255.  
  256. sub control_lpd {
  257.     my ($config) = $_[0];
  258.     
  259.     # Control the printing system with the standard "lpc" command
  260.  
  261.     # Control command
  262.     my $commandline = "$sysdeps->{'lpd-lpc'}";
  263.  
  264.     # Add the remaining command line arguments, they are the control command
  265.     # with its arguments
  266.  
  267.     $commandline .= " @ARGV";
  268.  
  269.     # Do it!
  270.  
  271.     #print "$commandline\n";
  272.     return (system $commandline) >> 8;
  273.  
  274. }
  275.  
  276. ### Printing/Job manipulation functions for LPRng
  277.  
  278. sub print_lprng {
  279.     my ($config) = $_[0];
  280.  
  281.     # Printing command
  282.     my $commandline = "$sysdeps->{'lpd-lpr'}";
  283.  
  284.     # Add the printer queue argument
  285.     if (defined($config->{'queue'})) {
  286.     $commandline .= " -P $config->{'queue'}";
  287.     }
  288.  
  289.     # Add the driver-specific options supplied by the user, if any
  290.     if ($#{$config->{'options'}} >= 0) {
  291.     for (@{$config->{'options'}}) {
  292.         $commandline .= " -Z $_";
  293.     }
  294.     }
  295.  
  296.     # Add the remaining command line arguments, they are the names of
  297.     # the files to print and also spooler-specific options
  298.     $commandline .= " @ARGV";
  299.  
  300.     # Do it!
  301.  
  302.     #print "$commandline\n";
  303.     return (system $commandline) >> 8;
  304.  
  305. }
  306.  
  307. sub query_lprng {
  308.     my ($config) = $_[0];
  309.  
  310.     # We filter the output of lpq and rearrange it to have the same format
  311.     # as of LPD and CUPS.
  312.  
  313.     GetOptions("l"   => \$opt_l);       # Long, more verbose output
  314.  
  315.     # List the jobs on the specified queue
  316.     my $queue = "";
  317.     if (defined($config->{'queue'})) {
  318.     $queue = " -P $config->{'queue'}";
  319.     }
  320.     open LPQOUTPUT, "$sysdeps->{'lpd-lpq'}$queue @ARGV |" || return 1;
  321.     my @lpqoutput = <LPQOUTPUT>;
  322.     close LPQOUTPUT;
  323.     # Filter the output
  324.     for $line (@lpqoutput) {
  325.     chomp $line;
  326.     if ($line =~ m!^\s*(\S+)\s+([^@\s]+)@[^@\+\s]+\+[0-9]+\s+\S+\s+([0-9]+)\s+(\S+)\s+([0-9]+)\s+[0-9:]+\s*$!) {
  327.         my ($rank, $owner, $jobid, $file, $size) = ($1, $2, $3, $4, $5);
  328.         if (defined($opt_l)) {
  329.         my $owner_rank = "$owner: $rank";
  330.         if (length($owner_rank) > 40) {
  331.             $owner_rank = substr($owner_rank, 0, 40);
  332.         }
  333.         if (length($file) > 40) {$file = substr($file, 0, 40);}
  334.         print sprintf("\n%-40s [job %d]\n\t%-40s %d bytes\n",
  335.                   $owner_rank, $jobid, $file, $size);
  336.         } else {
  337.         if (length($rank) > 6) {$rank = substr($rank, 0, 6)};
  338.         if (length($owner) > 8) {$owner = substr($owner, 0, 8)};
  339.         if (length($file) > 37) {$file = substr($file, 0, 37)};
  340.         print sprintf("%-6s %-8s % 6d %-37s %d bytes\n",
  341.                   $rank, $owner, $jobid, $file, $size);
  342.         }
  343.     } elsif ($line =~ m!\s*Rank\s+Owner!) {
  344.         if (!defined($opt_l)) {
  345.         print "Rank   Owner       Job File(s)                               Total Size\n";
  346.         }
  347.     } else {
  348.         print("$line\n");
  349.     }
  350.     }
  351. }
  352.  
  353. sub remove_lprng {
  354.     my ($config) = $_[0];
  355.     
  356.     # Remove a job with the standard "lprm" command and emulate the "-"
  357.     # option of the lprm command of BSD LPD
  358.  
  359.     # Removing command
  360.     my $commandline = "$sysdeps->{'lpd-lprm'}";
  361.  
  362.     # Add the printer queue argument
  363.     if (defined($config->{'queue'})) {
  364.     $commandline .= " -P $config->{'queue'}";
  365.     }
  366.  
  367.     # Replace the "-" option by the "all" option
  368.     my $alljobs = "";
  369.     for ($i = 0; ($i <= $#ARGV); $i++) {
  370.     if ($ARGV[$i] =~ m!^\s*\-\s*$!) {
  371.         $alljobs = " all";
  372.         splice(@ARGV,$i,1);
  373.         $i--;
  374.     }
  375.     }
  376.     $commandline .= $alljobs;
  377.  
  378.     # Add the remaining command line arguments, they are the numbers
  379.     # of the jobs to kill, the users whose jos to remove and also
  380.     # spooler-specific options
  381.  
  382.     $commandline .= " @ARGV";
  383.  
  384.     # Do it!
  385.  
  386.     #print "$commandline\n";
  387.     return (system $commandline) >> 8;
  388.  
  389. }
  390.  
  391. sub control_lprng {
  392.  
  393.     # The lpc command of lprng is compatible to the one of LPD, it has only
  394.     # many more commands. So we use the "control_lpd" function also for
  395.     # lprng.
  396.  
  397. }
  398.  
  399. ### Printing/Job manipulation functions for CUPS
  400.  
  401. sub print_cups {
  402.     my ($config) = $_[0];
  403.  
  404.     # Printing command
  405.     my $commandline = "$sysdeps->{'cups-lpr'}";
  406.  
  407.     # Add the printer queue argument
  408.     if (defined($config->{'queue'})) {
  409.     $commandline .= " -P $config->{'queue'}";
  410.     }
  411.  
  412.     # Add the driver-specific options supplied by the user, if any
  413.     if ($#{$config->{'options'}} >= 0) {
  414.     for (@{$config->{'options'}}) {
  415.         $commandline .= " -o $_";
  416.     }
  417.     }
  418.  
  419.     # Add the remaining command line arguments, they are the names of
  420.     # the files to print and also spooler-specific options
  421.     $commandline .= " @ARGV";
  422.  
  423.     # Do it!
  424.  
  425.     #print "$commandline\n";
  426.     return (system $commandline) >> 8;
  427.  
  428. }
  429.  
  430. sub query_cups {
  431.     my ($config) = $_[0];
  432.  
  433.     # List the jobs on the specified queue
  434.     my $queue = "";
  435.     if (defined($config->{'queue'})) {
  436.     $queue = " -P $config->{'queue'}";
  437.     }
  438.     return (system "$sysdeps->{'cups-lpq'}$queue @ARGV") >> 8;
  439.  
  440. }
  441.  
  442. sub remove_cups {
  443.     my ($config) = $_[0];
  444.     
  445.     # Remove a job with the standard "lprm" command
  446.  
  447.     # Removing command
  448.     my $commandline = "$sysdeps->{'cups-lprm'}";
  449.  
  450.     # Add the printer queue argument
  451.     if (defined($config->{'queue'})) {
  452.     $commandline .= " -P $config->{'queue'}";
  453.     }
  454.  
  455.     # Add the remaining command line arguments, they are the numbers
  456.     # of the jobs to kill, the users whose jos to remove and also
  457.     # spooler-specific options
  458.  
  459.     $commandline .= " @ARGV";
  460.  
  461.     # Do it!
  462.  
  463.     #print "$commandline\n";
  464.     return (system $commandline) >> 8;
  465.  
  466. }
  467.  
  468. sub control_cups {
  469.     my ($config) = $_[0];
  470.  
  471.     # CUPS has no LPD/LPRng-compatible lpc command, so we must emulate
  472.     # this functionality with the command line tools of CUPS.
  473.  
  474.     # The first command line argument (of the remaining ones) is the
  475.     # control command (standard commands of lpc for LPD/LPRng)
  476.  
  477.     my $command = shift (@ARGV);
  478.  
  479.     if (!defined($command)) {
  480.     die "You must supply a control command with the \"-C\" option!\n";
  481.     } elsif (lc($command) eq "up") { # Turn on queue (queueing/printing)
  482.     return (system "$sysdeps->{'cups-enable'} @ARGV; $sysdeps->{'cups-accept'} @ARGV") >> 8;
  483.     } elsif (lc($command) eq "down") { # Turn off queue (queueing/printing)
  484.     return (system "$sysdeps->{'cups-disable'} @ARGV; $sysdeps->{'cups-reject'} @ARGV") >> 8;
  485.     } elsif (lc($command) eq "start") { # Turn on queue (printing)
  486.     return (system "$sysdeps->{'cups-enable'} @ARGV") >> 8;
  487.     } elsif (lc($command) eq "stop") { # Turn off queue (printing)
  488.     return (system "$sysdeps->{'cups-disable'} @ARGV") >> 8;
  489.     } elsif (lc($command) eq "enable") { # Accept new jobs
  490.     return (system "$sysdeps->{'cups-accept'} @ARGV") >> 8;
  491.     } elsif (lc($command) eq "disable") { # Reject new jobs
  492.     return (system "$sysdeps->{'cups-reject'} @ARGV") >> 8;
  493.     } elsif (lc($command) eq "move") { # Move jobs
  494.     if (($#ARGV < 1) or ($#ARGV > 2)) {
  495.         die "Usage of the \"move\" control command:\n\n   move oldqueue [ jobID ] newqueue\n\n";
  496.     }
  497.     # The first argument is always the source printer
  498.     my $fromqueue = shift (@ARGV);
  499.     # The second argument is the job ID or the destination
  500.     my $jobid = shift (@ARGV);
  501.     # The third argument is the destination
  502.     my $toqueue = shift (@ARGV);
  503.     if (!defined($toqueue)) {
  504.         # No job ID given, move all jobs in the given queue
  505.         $toqueue = $jobid;
  506.         open LINES, "$sysdeps->{'cups-lpq'} -P $fromqueue |";
  507.         my @lines = <LINES>;
  508.         close LINES;
  509.         for (@lines) {
  510.         if ($_ =~ m!^\s*\S+\s+\S+\s+([0-9]+)\s+!) {
  511.             system "$sysdeps->{'cups-lpmove'} $fromqueue-$1 $toqueue";
  512.         }
  513.         }
  514.         return;
  515.     } else {
  516.         # Treat the specified job
  517.         return (system "$sysdeps->{'cups-lpmove'} $fromqueue-$jobid $toqueue") >> 8;
  518.     }
  519.     } elsif ((lc($command) eq "hold") ||    # Hold job
  520.          (lc($command) eq "release") || # Resume job
  521.          (lc($command) eq "topq")) {    # Bring job to the top of the
  522.                                         # queue
  523.     if (($#ARGV < 0) or ($#ARGV > 1)) {
  524.         die "Usage of the \"$command\" control command:\n\n   $command queue [ jobID ] \n\n";
  525.     }
  526.     # Clean up the command
  527.     $command = lc($command);
  528.     if ($command eq "release") {$command = "resume";} 
  529.     if ($command eq "topq") {$command = "immediate";} 
  530.     # The first argument is always the queue
  531.     my $queue = shift (@ARGV);
  532.     # The second argument is the job ID
  533.     my $jobid = shift (@ARGV);
  534.     if (!defined($jobid)) {
  535.         # No job ID given, treat all jobs in the given queue
  536.         open LINES, "$sysdeps->{'cups-lpq'} -P $queue |";
  537.         my @lines = <LINES>;
  538.         close LINES;
  539.         for (@lines) {
  540.         if ($_ =~ m!^\s*\S+\s+\S+\s+([0-9]+)\s+!) {
  541.             system "$sysdeps->{'cups-lp'} -i $queue-$1 -H $command";
  542.         }
  543.         }
  544.         return;
  545.     } else {
  546.         # Treat the specified job
  547.         return (system "$sysdeps->{'cups-lp'} -i $queue-$jobid -H $command") >> 8;
  548.     }
  549.     } elsif (lc($command) eq "status") { # Queue status listing
  550.     return (system "$sysdeps->{'cups-lpc'} status @ARGV") >> 8;
  551.     } elsif (lc($command) eq "help") { # List the available commands
  552.     print "The following control commands are available:\n\n";
  553.     print "   up queue                : Turn on queue (queueing/printing)\n";
  554.     print "   down queue              : Turn off queue (queueing/printing)\n";
  555.     print "   start queue             : Turn on printing on queue\n";
  556.     print "   stop queue              : Turn off printing on queue\n";
  557.     print "   enable queue            : Make queue accepting new jobs\n";
  558.     print "   disable queue           : Make queue rejecting new jobs\n";
  559.     print "   move oldqueue [ jobid ] newqueue : \n";
  560.     print "       Move job jobid in oldqueue to newqueue\n";
  561.     print "       Move all jobs in oldqueue to newqueue when jobid not given\n";
  562.     print "   hold queue [ jobid ]    : Hold job jobid or all jobs in queue\n";
  563.     print "   release queue [ jobid ] : Release job jobid or all jobs in queue\n";
  564.     print "   topq queue jobid        : Print job jobid in queue immediately\n";
  565.     print "   status [ queue ]        : Status of queue or of all queues\n";
  566.     print "   help                    : This help message\n\n";
  567.     } else {
  568.     die "Command \"$command\" not recognized!\n";
  569.     }
  570.     
  571. }
  572.  
  573. ### Printing/Job manipulation functions for PDQ
  574.  
  575. sub print_pdq {
  576.     my ($config) = $_[0];
  577.  
  578.     # Printing command
  579.     my $commandline = "$sysdeps->{'pdq-print'}";
  580.  
  581.     # Add the printer queue argument
  582.     if (defined($config->{'queue'})) {
  583.     $commandline .= " -P $config->{'queue'}";
  584.     }
  585.  
  586.     # Add the driver-specific options supplied by the user, if any
  587.     if ($#{$config->{'options'}} >= 0) {
  588.     for (@{$config->{'options'}}) {
  589.         my $option = $_;
  590.         if ($option =~ m!^\s*([^=]+=[\+\-0-9\.]+)\s*$!) {
  591.         # Foomatic treats numerical options as PDQ arguments ("-a"),
  592.         # but there can be enumerated options with numbers as choices,
  593.         # so we give the option in both styles. Since PDQ silently
  594.         # ignores non-existent options, the wrong form of the option
  595.         # will be ignored.
  596.         $commandline .= " -aOPT_$1";
  597.         }
  598.         # Enumerated and boolean options are PDQ options ("-o"),
  599.         # the "=" has to be replaced by "_" to work with the
  600.         # PDQ-O-MATIC-generated configuration
  601.         $option =~ s/=/_/;  # Replace only the first "="
  602.         $commandline .= " -o$option";
  603.     }
  604.     }
  605.  
  606.     # The "-#" option for multiple copies is not supported by the print
  607.     # command "pdq". So we launch "pdq" once per copy. Thw command line
  608.     # will be modified appropriately directly before the printing command
  609.     # is launched.
  610.     # Note: '#' as option name is not supported by the Perl library
  611.     # Getopt::Long.
  612.     my $num_copies = 1;
  613.     my $file_in_args = 0;
  614.     my $i;
  615.     for ($i = 0; ($i <= $#ARGV); $i++) {
  616.     if ($ARGV[$i] =~ m!^\s*\-\#\s*([0-9]+)\s*$!) {
  617.         $num_copies = $1;
  618.         splice(@ARGV,$i,1);
  619.         $i--;
  620.     } elsif ($ARGV[$i] =~ m!^\s*\-\#\s*$!) {
  621.         if ((defined $ARGV[$i+1]) && 
  622.         ($ARGV[$i+1] =~ m!^\s*([0-9]+)\s*$!)) {
  623.         $num_copies = $1;
  624.         splice(@ARGV,$i,2);
  625.         $i--;
  626.         }
  627.     } elsif ($ARGV[$i] =~ m!^\s*[^\-]+!) {
  628.         $file_in_args = 1;
  629.     }
  630.     }
  631.  
  632.     # Add the remaining command line arguments, they are the names of
  633.     # the files to print and also spooler-specific options
  634.     $commandline .= " @ARGV";
  635.  
  636.     # Do it!
  637.     #print "$commandline\n"; return 0;
  638.  
  639.     if ($num_copies == 1) {
  640.     return (system $commandline) >> 8;
  641.     } else {
  642.     if ($file_in_args == 0) {
  643.         # We print from standard input, so we must buffer it to be able
  644.         # to print multiple copies
  645.         my @job_contents = <STDIN>;
  646.         my $i;
  647.         for ($i = 0; $i < $num_copies; $i++) {
  648.         open PIPE, "| $commandline" || 
  649.             die "Could not launch printing command!\n";
  650.         print PIPE @job_contents;
  651.         close PIPE;
  652.         }
  653.         return 0;
  654.     } else {
  655.         # We print files
  656.         my $result = 0;
  657.         my $i;
  658.         for ($i = 0; $i < $num_copies; $i++) {
  659.         $result = (system $commandline) >> 8;
  660.         if ($result != 0) {return $result};
  661.         }
  662.         return 0;
  663.     }
  664.     }
  665. }
  666.  
  667. sub query_pdq {
  668.     my ($config) = $_[0];
  669.  
  670.     # PDQ has no possiblity to list the printing jobs from the command
  671.     # line.  So we read the *.status files in ~/.printjobs and generate
  672.     # the job entry lines from that information.
  673.  
  674.     # Read additional options
  675.     GetOptions("a"   => \$opt_a,        # List jobs on all printers
  676.            "l"   => \$opt_l);       # Long, more verbose output
  677.  
  678.     # Make sure that a printer is specified when the "-a" option is not
  679.     # given
  680.     if ((!(defined($opt_a))) && (!(defined($config->{'queue'})))) {
  681.     $config->{'queue'} = get_pdq_default_printer();
  682.     }
  683.  
  684.     # If the user specified job numbers, list them. User names on the
  685.     # command line do not make much sense, because under PDQ a user can
  686.     # only see ones own jobs, they are supported here to do not break
  687.     # front ends
  688.     my $joblist = {};
  689.     my $userlist = {};
  690.     my $listalljobs = 1;
  691.     my $listallusers = 1;
  692.     my $i;
  693.     for ($i = 0; ($i <= $#ARGV); $i++) {
  694.     if ($ARGV[$i] =~ m!^\s*([0-9]+)\s*$!) {
  695.         my $job=$1;
  696.         # Fill up the number with zeros so that it has three digits
  697.         while (length($job) < 3) {$job = "0" . $job;}
  698.         $joblist->{$job} = 1;
  699.         $listalljobs = 0;
  700.         splice(@ARGV,$i,1);
  701.         $i--;
  702.     } elsif ($ARGV[$i] =~ m!^\s*[^\-]+!) {
  703.         my $user=$ARGV[$i];
  704.         $userlist->{$user} = 1;
  705.         $listallusers = 0;
  706.         splice(@ARGV,$i,1);
  707.         $i--;
  708.     } else {
  709.         die "Unknown option: $ARGV[$i]\n";
  710.     }
  711.     }
  712.  
  713.     # When we list only the jobs for a specific printer, display the
  714.     # printer status at first. In PDQ the printer status cannot be
  715.     # retrived from the command line, so we put a dummy line
  716.     # "<printer> is ready".
  717.     if (!(defined($opt_a))) {
  718.     if (!pdq_check_printer($config->{'queue'})) {
  719.         die "$config->{'queue'}: unknown printer\n";
  720.     }
  721.     print "$config->{'queue'} is ready\n";
  722.     }
  723.  
  724.     # Read in the names of all job status files in ~/.printjobs/
  725.     my @jobnumbers = ();
  726.     opendir PJDIR, "$ENV{'HOME'}/.printjobs" || 
  727.     return 0;  # No ~/.printjobs/ directory ==> no jobs
  728.     while ($filename = readdir(PJDIR)) {
  729.     if ($filename =~ m!^([0-9][0-9][0-9]).status$!) {
  730.         push (@jobnumbers, $1);
  731.     }
  732.     }
  733.     close PJDIR;
  734.     # Sort the filenames in descending order to get the most recent jobs
  735.     # listed at first
  736.     @jobnumbers = sort {$b cmp $a} @jobnumbers;
  737.  
  738.     # Now list the jobs
  739.     my $firstline = 1;
  740.     for ($i = 0; $i <= $#jobnumbers; $i ++) {
  741.     # Omit this job if job numbers are specified on the command line, but
  742.     # not the one of this job
  743.     next if (($listalljobs == 0) && 
  744.          (!(defined($joblist->{$jobnumbers[$i]}))));
  745.     # Read the job status file
  746.     next if !open JOBSTATUSFILE, 
  747.     "< $ENV{'HOME'}/.printjobs/$jobnumbers[$i].status";
  748.     my $jobstatusdata = join("", <JOBSTATUSFILE>);
  749.     close JOBSTATUSFILE;
  750.     # Extract the important fields from the file
  751.     # Status:
  752.     my $status = "";
  753.     if ($jobstatusdata =~ m!^\s*status\s*\=\s*{([^{}]*)}\s*$!m) {
  754.         $status = $1;
  755.     }
  756.     # Omit this job when it has no status field or when the job is
  757.     # already finished, cancelled, or aborted
  758.     next if (($status eq "") || ($status =~ m!aborted!) || 
  759.          ($status =~ m!finished!) || ($status =~ m!cancelled!));
  760.     # Avoid spaces in the status field, so that frontends can separate the
  761.     # fields from the job list more easily.
  762.     $status =~ s/\s//g;
  763.     # Printer
  764.     my $printer;
  765.     if ($jobstatusdata =~ m!^\s*printer\s*\=\s*{([^{}]*)}\s*$!m) {
  766.         $printer = $1;
  767.     }
  768.     # Omit this job when we are querying only the jobs of another printer
  769.     next if ((!(defined($opt_a))) && ($printer ne $config->{'queue'}));
  770.     # Owner
  771.     my $owner;
  772.     if ($jobstatusdata =~ 
  773.     m!^\s*env_driver\s*\=\s*{.*\"LOGNAME\"\s*=\s*\"([^\"]*)\".*}\s*$!m) {
  774.         $owner = $1;
  775.     }
  776.     # Omit this job if user names are specified on the command line, but
  777.     # not the owner of this job
  778.     next if (($listallusers == 0) && 
  779.          (!(defined($userlist->{$owner}))));
  780.     # File
  781.     my $file;
  782.     if ($jobstatusdata =~ m!^\s*input_filename\s*\=\s*{([^{}]*)}\s*$!m) {
  783.         $file = $1;
  784.     }
  785.     # Size of job input file
  786.     my $size;
  787.     if (-f "$ENV{'HOME'}/.printjobs/$jobnumbers[$i].raw") {
  788.         $size = (stat("$ENV{'HOME'}/.printjobs/$jobnumbers[$i].raw"))[7];
  789.     }
  790.  
  791.     # Now get the info nicely onto the screen
  792.     my $outputline;
  793.     if ($opt_l) {
  794.         # Long (3+ lines per job) mode
  795.         my $owner_status = "$owner: $status";
  796.         if (length($owner_status) > 40) {
  797.         $owner_status = substr($owner_status, 0, 40);
  798.         }
  799.         if (length($file) > 40) {$file = substr($file, 0, 40);}
  800.         $outputline = sprintf("\n%-40s [job %d]\n\t%-40s %d bytes\n",
  801.                   $owner_status, $jobnumbers[$i], $file,
  802.                   $size);
  803.     } else {
  804.         # Short (1 line per job) mode
  805.         if ($firstline == 1) {
  806.         # headline
  807.         print "Rank   Owner      Job  File(s)                               Total Size\n";
  808.         $firstline = 0;
  809.         }
  810.         if (length($status) > 6) {$status = substr($status, 0, 6);}
  811.         if (length($owner) > 10) {$owner = substr($owner, 0, 10);}
  812.         if (length($file) > 37) {$file = substr($file, 0, 37);}
  813.         $outputline = sprintf("%-6s %-10s % 3d  %-37s %d bytes\n",
  814.                   $status, $owner, $jobnumbers[$i], $file,
  815.                   $size);
  816.     }
  817.     print $outputline;
  818.     }
  819.  
  820.     # Say "no entries" if no job was listed
  821.     if ($firstline == 1) {
  822.     print "no entries\n";
  823.     }
  824.     
  825. }
  826.  
  827. sub remove_pdq {
  828.     my ($config) = $_[0];
  829.  
  830.     # PDQ has no possiblity to remove printing jobs from the command
  831.     # line.  "xpdq" cancels jobs by "touch"ing <job id>.cancelled
  832.     # files in ~/.printjobs and setting the permissions of these files
  833.     # to 0600.
  834.  
  835.     # Make sure that a printer is specified when the "-a" option is not
  836.     # given
  837.     if (!(defined($config->{'queue'}))) {
  838.     $config->{'queue'} = get_pdq_default_printer();
  839.     }
  840.  
  841.     # If the user specified job numbers, list them. User names on the
  842.     # command line do not make much sense, because under PDQ a user can
  843.     # only see ones own jobs, they are supported here to do not break
  844.     # front ends
  845.     my $joblist = {};
  846.     my $userlist = {};
  847.     my $nojob = 1;
  848.     my $nouser = 1;
  849.     my $opt_alljobs = 0;
  850.     my $i;
  851.     for ($i = 0; ($i <= $#ARGV); $i++) {
  852.     if ($ARGV[$i] =~ m!^\s*([0-9]+)\s*$!) {
  853.         my $job=$1;
  854.         # Fill up the number with zeros so that it has three digits
  855.         while (length($job) < 3) {$job = "0" . $job;}
  856.         $joblist->{$job} = 1;
  857.         $nojob = 0;
  858.         splice(@ARGV,$i,1);
  859.         $i--;
  860.     } elsif ($ARGV[$i] =~ m!^\s*[^\-]+!) {
  861.         my $user=$ARGV[$i];
  862.         $userlist->{$user} = 1;
  863.         $nouser = 0;
  864.         splice(@ARGV,$i,1);
  865.         $i--;
  866.     } elsif ($ARGV[$i] =~ m!^\s*\-\s*$!) {
  867.         $opt_alljobs = 1;
  868.         splice(@ARGV,$i,1);
  869.         $i--;
  870.     } else {
  871.         die "Unknown option: $ARGV[$i]\n";
  872.     }
  873.     }
  874.  
  875.     # Does the chosen printer exist
  876.     if (!pdq_check_printer($config->{'queue'})) {
  877.     die "$config->{'queue'}: unknown printer\n";
  878.     }
  879.  
  880.     # Read in the names of all job status files in ~/.printjobs/
  881.     my @jobnumbers = ();
  882.     opendir PJDIR, "$ENV{'HOME'}/.printjobs" || 
  883.     return 0;  # No ~/.printjobs/ directory ==> no jobs
  884.     while ($filename = readdir(PJDIR)) {
  885.     if ($filename =~ m!^([0-9][0-9][0-9]).status$!) {
  886.         push (@jobnumbers, $1);
  887.     }
  888.     }
  889.     close PJDIR;
  890.     # Sort the filenames in descending order to get the most recent
  891.     # (probably still waiting) jobs removed at first
  892.     @jobnumbers = sort {$b cmp $a} @jobnumbers;
  893.  
  894.     # Now search the jobs to remove
  895.     my $nothingremoved = 1;
  896.     my $mostrecent = 1;
  897.     for ($i = 0; $i <= $#jobnumbers; $i ++) {
  898.     # Read the job status file
  899.     next if !open JOBSTATUSFILE, 
  900.     "< $ENV{'HOME'}/.printjobs/$jobnumbers[$i].status";
  901.     my $jobstatusdata = join("", <JOBSTATUSFILE>);
  902.     close JOBSTATUSFILE;
  903.     # Extract the important fields from the file
  904.     # Status:
  905.     my $status = "";
  906.     if ($jobstatusdata =~ m!^\s*status\s*\=\s*{([^{}]*)}\s*$!m) {
  907.         $status = $1;
  908.     }
  909.     # Omit this job when it is already finished, cancelled, or aborted
  910.         # (then it cannot be killed any more)
  911.     next if (($status eq "") || ($status =~ m!aborted!) || 
  912.          ($status =~ m!finished!) || ($status =~ m!cancelled!));
  913.     # Printer
  914.     my $printer;
  915.     if ($jobstatusdata =~ m!^\s*printer\s*\=\s*{([^{}]*)}\s*$!m) {
  916.         $printer = $1;
  917.     }
  918.     # Omit this job when we want to remove jobs on another printer
  919.     next if ((!(defined($opt_a))) && ($printer ne $config->{'queue'}));
  920.     # Owner
  921.     my $owner;
  922.     if ($jobstatusdata =~ 
  923.     m!^\s*env_driver\s*\=\s*{.*\"LOGNAME\"\s*=\s*\"([^\"]*)\".*}\s*$!m) {
  924.         $owner = $1;
  925.     }
  926.  
  927.     # Kill the job when it is in the scope of jobs defined by the
  928.     # command line
  929.     if ((($nojob == 0) && (defined($joblist->{$jobnumbers[$i]}))) || 
  930.         (($nouser == 0) && (defined($userlist->{$owner}))) ||
  931.         (($opt_alljobs == 1) && ($ENV{'LOGNAME'} eq $owner)) ||
  932.         (($opt_alljobs == 1) && ($ENV{'LOGNAME'} eq "root")) ||
  933.         (($mostrecent == 1) && ($nojob == 1) && ($nouser == 1) && 
  934.          ($opt_alljobs == 0))) {
  935.         system("touch $ENV{'HOME'}/.printjobs/$jobnumbers[$i].cancelled; chmod 0600 $ENV{'HOME'}/.printjobs/$jobnumbers[$i].cancelled");
  936.         print STDERR "Cancel request for job $jobnumbers[$i] submitted!\n";
  937.         $nothingremoved = 0;
  938.     }
  939.     $mostrecent = 0;
  940.     }
  941.  
  942.     # Say "No cancel request sent" if no job was killed
  943.     if ($nothingremoved == 1) {
  944.     print STDERR "no cancel request sent\n";
  945.     }
  946. }
  947.  
  948. sub control_pdq {
  949.  
  950.     # PDQ does not have functionality for enabling/disabling queues, 
  951.     # holding/releasing/moving jobs, etc.
  952.  
  953.     die "Advanced queue/job manipulation functionality is not supported under PDQ!\n";
  954.  
  955. }
  956.  
  957. sub get_pdq_default_printer {
  958.  
  959.     # Read the help message of PDQ
  960.     open PDQHELP, "pdq --help 2>&1 |";
  961.     $pdqhelp = join ("", <PDQHELP>);
  962.     close PDQHELP;
  963.  
  964.     # Search the "default" line
  965.     if ($pdqhelp =~ m!default\s+printer.*\s+(\S+)\s*$!mg) {
  966.     return $1;
  967.     } else {
  968.     die "No default printer defined, you have to specify a printer with \"-P\" or \"-d\"!\n";
  969.     }
  970.  
  971. }
  972.  
  973. sub pdq_check_printer {
  974.     my $printer = $_[0];
  975.  
  976.     # Read the help message of PDQ
  977.     open PDQHELP, "pdq --help 2>&1 |";
  978.     $pdqhelp = join ("", <PDQHELP>);
  979.     close PDQHELP;
  980.  
  981.     # Search the appropriate printer entry
  982.     return ($pdqhelp =~ m!^\s+$printer\s+\-\s+.*\s+\-\s*$!mg);
  983.  
  984. }
  985.  
  986. sub detect_spooler {
  987.     # If tcp/localhost:631 opens, cups
  988.     my $page = $db->getpage('http://localhost:631/', 1);
  989.     if ($page =~ m!Common UNIX Printing System!) {
  990.     return 'cups';
  991.     }
  992.  
  993.     # Else if /etc/printcap, some sort of lpd thing
  994.     if (-f $sysdeps->{'lpd-pcap'}) {
  995.     # If -f /etc/lpd.conf, lprng
  996.     if (-f $sysdeps->{'lprng-conf'}) {
  997.         return 'lprng';
  998.     } elsif (-x $sysdeps->{'lpd-bin'}) {
  999.         # There's a /usr/sbin/lpd
  1000.         return 'lpd';
  1001.     }
  1002.     }
  1003.  
  1004.     # pdq executable in our path somewhere?
  1005.     for (split(':', $ENV{'PATH'})) {
  1006.     if (-x "$_/pdq") {
  1007.         return 'pdq';
  1008.     }
  1009.     }
  1010.  
  1011.     return undef;
  1012. }
  1013.  
  1014. sub unimp {
  1015.     die "Sorry, $action for your spooler is unimplemented...\n";
  1016. }
  1017.  
  1018. sub help {
  1019.  
  1020.     my $action = 'all';
  1021. # Set up the help message depending on how we were called
  1022.     if ($progname =~ m!^lpc!) { # 'lpc*' ==> control 
  1023.     $action = 'control';
  1024.     print STDERR <<EOF;
  1025. Usage: $progname [ -s spooler ] [ -i ] command [ arguments ]
  1026.     or $progname -h
  1027. EOF
  1028.     } elsif ($progname =~ m!^lprm!) { # 'lprm*' ==> remove jobs 
  1029.     $action = 'remove';
  1030.     print STDERR <<EOF; 
  1031. Usage: $progname [ -s spooler ] [ -P queuename ] [ - ] [ -i ] [ jobid1 jobid2 ... ]
  1032.     or $progname -h
  1033. EOF
  1034.     } elsif ($progname =~ m!^lpq!) { # 'lpq*' ==> list jobs 
  1035.     $action = 'query';
  1036.     print STDERR <<EOF;
  1037. Usage: $progname [ -s spooler ] [ -P queuename ] [ -i ] [ -a ] [ user1 user2 ... ]
  1038.     or $progname -h
  1039. EOF
  1040.     } elsif (($progname =~ m!^lpr!) || ($progname =~ m!^lp!)) { # 'lpr*', 'lp*' ==> print 
  1041.     $action = 'print';
  1042.     print STDERR <<EOF;
  1043. Usage: $progname [ -s spooler ] [ -P queuename ] \
  1044.                [ -o option1=value1 -o option2 ... ] [ -i ] [ file1 file2 ... ]
  1045.     or $progname -S [ -s spooler ] [ -i ]
  1046.     or $progname -h [ -s spooler ] [ -P queuename ] [ -i ]
  1047. EOF
  1048.     } else { # name does not determine the action
  1049.     print STDERR <<EOF;
  1050. Usage: $progname [ -s spooler ] [ -P queuename ] \
  1051.                [ -o option1=value1 -o option2 ... ] [ -i ] \
  1052.            [ file1 file2 ... ]
  1053.     or $progname -Q [ -s spooler ] [ -P queuename ] [ -i ] [ -a ] \
  1054.            [ user1 user2 ... ]
  1055.     or $progname -R [ -s spooler ] [ -P queuename ] [ - ] [ -i ] \
  1056.            [ jobid1 jobid2 ... ]
  1057.     or $progname -C [ -s spooler ] [ -i ] command [ arguments ]
  1058.     or $progname -S [ -s spooler ] [ -i ]
  1059.     or $progname -h [ -s spooler ] [ -P queuename ] [ -i ]
  1060. EOF
  1061.     }
  1062.  
  1063.      print STDERR <<EOF;
  1064.  
  1065.  -s spooler      Explicit spooler type (cups,lpd,lprng,pdq)
  1066. EOF
  1067.  
  1068.     if ($action ne 'control') {
  1069.     print STDERR <<EOF;
  1070.  -P queuename    Command should apply to this queue
  1071. EOF
  1072.     }
  1073.  
  1074.     if (($action eq 'print') || ($action eq 'all')) {
  1075.     print STDERR <<EOF;
  1076.  -o option=value Set option to value
  1077.  -o option       Set the switch option
  1078.  -\# n            Print n copies
  1079.  file1 file2 ... Files to be printed, when no file is given, standard input
  1080.                  will be printed
  1081. EOF
  1082.     }
  1083.  
  1084.     if ($action eq 'all') {
  1085.     print STDERR <<EOF;
  1086.  -Q              Query the jobs in a queue
  1087. EOF
  1088.     }
  1089.  
  1090.     if (($action eq 'query') || ($action eq 'all')) {
  1091.     print STDERR <<EOF;
  1092.  -a              Query the jobs in all queues
  1093.  user1 user2 ... Users whose jobs should be listed
  1094. EOF
  1095.     }
  1096.  
  1097.     if ($action eq 'all') {
  1098.     print STDERR <<EOF;
  1099.  -R              Remove a job from a queue
  1100. EOF
  1101.     }
  1102.  
  1103.     if (($action eq 'remove') || ($action eq 'all')) {
  1104.     print STDERR <<EOF;
  1105.  -               Remove all your jobs
  1106.  jobid1 jobid2   IDs of the jobs to be removed
  1107. EOF
  1108.     }
  1109.  
  1110.     if ($action eq 'all') {
  1111.     print STDERR <<EOF;
  1112.  -C              Execute control commands for queue/job manipulation
  1113. EOF
  1114.     }
  1115.  
  1116.     if (($action eq 'control') || ($action eq 'all')) {
  1117.     print STDERR <<EOF;
  1118.  command [ arguments ]  Control command for queue/job manipulation. The 
  1119.                  commands are the ones of the BSD "lpc" utility. Use
  1120.          the control command "help" to get a list of supported 
  1121.                  commands. Note: the amount of commands varies with the
  1122.                  spooler, but the same commands given under different 
  1123.                  spoolers do the same thing.
  1124. EOF
  1125.     }
  1126.  
  1127.     print STDERR <<EOF;
  1128.  -i              Interactive mode: You will be asked if $progname
  1129.                  is in doubt about something. Otherwise $progname
  1130.          uses auto-detection or quits with an error.
  1131. EOF
  1132.  
  1133.     if (($action eq 'print') || ($action eq 'all')) {
  1134.     print STDERR <<EOF;
  1135.  -S              Save the chosen spooler as the default spooler
  1136.  -h              Show this message or show a list of available options if a 
  1137.                  queue is specified
  1138.  
  1139. EOF
  1140.     } else {
  1141.     print STDERR <<EOF;
  1142.  -h              Show this message
  1143.  
  1144. EOF
  1145.     }
  1146.  
  1147.     exit 0;
  1148. }
  1149.  
  1150. # Help on printer-specific options
  1151. sub help_options {
  1152.     my ($config) = $_[0];
  1153.     
  1154.     # Is there an easier way to do this?
  1155.     eval `foomatic-configure -P -n $config->{'queue'} -s $config->{'spooler'}`;
  1156.     print "Available options for queue $config->{'queue'}:\n";
  1157.     
  1158.     foreach my $arg (@{$QUEUES[0]->{'args'}}) {
  1159.         next if $arg->{'hidden'};
  1160.         my @vals = ();
  1161.  
  1162.         print "  $arg->{'name'} : < ";
  1163.         foreach my $val (@{$arg->{'vals'}}) {
  1164.             push @vals, $val->{'value'};
  1165.         }
  1166.         print join(' | ', @vals) . " >\n";
  1167.     }
  1168.  
  1169.     exit 0;
  1170. }
  1171.